Guide to a generic keygen for Soft-Guard 1.10
by sn00pee of BLiZZARD
=============================================

First I wanna mention that my english isn't good,
sorry for any grammatical or other faults :)

What we need
------------

- Our victim: A Simple Task Reminder, download it from
  http://www.somtel.com/~barrym/download.htm
  You can also use another Soft-Guard protected app.
- Numega SmartCheck
- W32DSM(with or without string patch, doesn't matter)
- masm32(or tasm if you want, but then you have to apply 
  some changes to the code)
- a working brain :)

Besides you must have 
- experience with W32DSM
- experience with SmartCheck
- experience with the Asm-Language

Yes! We really don't need SICE! ;)

What is Soft-Guard?
-------------------

What the developers say(taken from www.soft-guard.com):

Soft-Guard is an ActiveX control designed for use with Visual Basic         
v5.0/6.0 applications to easily implement characteristics of Shareware      
Applications into your Windows 95/98/NT programs.  Soft-Guard allows        
you to implement evaluation periods into your applications and also         
handles the task of generating unique registration codes to unlock          
your application's evaluation period.  Soft-Guard also now allows you       
to generate registration codes that are system sensitive (i.e. the          
code only works on a system that matches the same specifications).           

Soft-Guard keeps track of evaluation period information and registered      
users in the Windows system registry.  Evaluation information is            
stored three times in the system registry, all of which are encrypted       
using different methods.  Should a user attempt to edit the                 
information, the evaluation period will be voided as the three              
locations that the information is stored do not match.  Soft-Guard's        
features were designed to be easily implemented with only a few lines       
of code.

Soft-Guard generates a unique registration code based upon the user's       
name, an encryption key set by the programmer, and other optional           
choices.  Registration codes can be formatted to your own style based       
upon multiple settings.  The registration codes are mathematically          
calculated combining the user's name and the encryption key, and then       
formatted to your preferences.  Soft-Guard also allows for the              
registration codes to be date sensitive, which also calculates the          
date into the code.  The registration code will then only be valid on       
the date on which it was created.

What sn00pee says ;):
This text is a good overview...it sounds a bit complicated and difficult,
but believe me, it really isn't ;)
The *.ocx control(SOFTGUARD5.OCX, you can find it in your windows/system
dir, assumed you installed the program) is entirelly written in 
Visual Basic 5, compiled to Native Code and has no anti-debbuging 
routines. So we can use Numega SmartCheck for cracking. 

Version compatibility
---------------------
As far as i knowm,  there is only one version of soft-guard out there
(1.10, the one we use), I really never had something to do with other
ones.

Why a generic keygen?
---------------------
Well, commercial protection schemes are very susceptile to generic
patches/keygens etc., you can save a lot of time and work and sometimes 
even money when using them :D. Of course you can reverse the whole thing
(btw: reversing the softguard-ocx is quite easy, you just need to invest 
enough time), but since we wanna expend our horizons, we code a generic
keygen. And btw, it sounds much better ;).

I tested this keygen with several protected programs, it works with and
without system depending serials and should work with date sensitive
ones too(don't blame me if it doesn't ;)).

Information about our victim
----------------------------
Not much to say, it uses system depending serials, what means that the
serial will only work on YOUR PC with YOUR unique System ID(soft-guard
uses some weird algo to calculate it).

Step 1: Cracking(not very long :))
----------------------------------
Enough of boring texts, let's rock :) ...
Fire up Smart-Check, open the str.exe and start it.
Click on yes and you should get a Registration-Dialog with the program 
name as caption. Enter a name and a dummy serial, click on Ok and you
will get an error message saying that the entered serial is wrong.
Switch back to SmartCheck and scroll down to the MsgBox-Call. Now we
need to find the right serial. Scroll up(be sure you have enabled
"Show All Events") until you see the following line(it's at position 
96234 but this number can differ on your pc):
__vbFreeVar(VARIANT:String:<RIGHTSERIAL>) returns DWORD:40
There we have our right serial. But how do we do a generic keygen now?
Hmm, let's do some brain-storming ;)...no matter what name/serial 
you enter and no matter which soft-guard protected application you wanna
crack, it always passes the call above, because it has to free 
the string with the right serial. So why don't we code a little program 
that gains control over the softguard5.ocx and "catches" this 
position(for now, I will call it the "breakpoint") and copies the right 
serial.
You don't know how to do that? Not bad, I will teach you how:

Step 2: Coding
--------------
We will do it in asm(masm), if you don't know asm then go
and search some tutorials, because: no asm -> no cracking ;). 
Basically, there are 2 different ways to solve our problem:
1) Using Debug-APIs - This is quite complicated and unreliable.
2) The ultimate sn00pee-style :P
Guess what method we'll use ;)
The idea is to start the program via "CreateProcess", write and endless
loop at the breakpoint and then read out the right serial. To 
write this loop, we use "WriteProcessMemory" and "VirtualProtectEx".
You should be familiar to them, if not then refer to an win32-help 
file. When the program reaches this loop, it won't do anything anymore,
it is "freezed". Then our keygen comes into the spotlight ;), it will
constantly check if the program is freezed and, if so, read out the right 
serial by getting the register content(in our case this is ecx, we'll talk
about this later). To get the register contents we use 
"GetThreadContext", here is the function-proto:
BOOL GetThreadContext(
  HANDLE hThread,       // handle to thread with context
  LPCONTEXT lpContext   // address of context structure
);
We will get hThread after calling CreateProcess, so we only have to 
declare a CONTEXT structure(I won't list its members here, because it's
a very complex structure, don't care about it).

Now we need our breakpoint, switch back to SmartCheck and click on
the __vbFreeVar line, in the upper-right corner we see an RVA(Relative 
Virtual Address, means nothing more than a memory-address). Write it 
down(in our case "19B96"), fire up W32DSM and disassemble the
softguard5.ocx(it is in your windows\system dir), now go to 
code location 11019B96(remember: code address = Imagebase + RVA), you
should see the following code:

* Reference To: MSVBVM50.__vbaFreeStr, Ord:0000h
                                  |
:11019B8C FF1550140311            Call dword ptr [11031450]
:11019B92 8D4D84                  lea ecx, dword ptr [ebp-7C]

* Reference To: MSVBVM50.__vbaFreeVar, Ord:0000h
                                  |
:11019B95 FF1558120311            Call dword ptr [11031258]

Do you see the same as me? "lea ecx, dword ptr [ebp-7C]", this is the 
important instruction, after executing this one, ecx will point to
the right serial, right? WRONG! Unfortunately, Visual Basic uses
some weird parameter-conventions for its functions. After tracing 
through the code with SoftICE, I found out that the pointer to the
right serial will be 8 bytes after the ECX, what means we have to
get the value in ecx, add 8 to it(ecx+8) and copy the pointer at this
address(4 bytes long). That's it! Back to the code, now we know that 
our breakpoint is "11019B95".
This is all the information we need! I will now list the complete
asm code for our keygen. You have to execute the keygen with the 
soft-guard protected applications location as command line.
(f.e. if you program is located in c:\program files\appname\app.exe
then you have to enter "keygen.exe c:\program files\appname\app.exe")

------------------------------ CUT ------------------------------ 

.386
.model flat, stdcall
option	casemap:none
;--------------------------[ Includes ]--------------------------
include		windows.inc
include		kernel32.inc
include		user32.inc
;--------------------------[ Libraries ]-------------------------
includelib	kernel32.lib
includelib	user32.lib
includelib	gdi32.lib

;--------------------[ Initialized Variables ]-------------------
.data
Breakpoint		dd 11019B95h	;Breakpoint
SaveBuffer		db 2 dup(0)	;SaveBuffer
JmpBuffer		db 0EBh,0FEh	;opcode for jmp -2
					;->endless loop
CommandLine		db " ",0

;error texts
err_caption		db "Error",0
err_open_text		db "Could not execute file!",0
err_notsg_text		db "This is not a Soft-Guard 1.10 "
			db "protected program!",0
err_readmem_text 	db "Could not read from memory!",0
err_writemem_text 	db "Could not write to memory!",0
mb_success_capt 	db "Your Serial",0

Serial			db 255 dup(0)
tempSerial		db 255 dup(0)

StartupInfo		STARTUPINFO {SIZEOF STARTUPINFO,NULL,NULL,
			NULL,0,0,0,0,0,0,0,STARTF_USESHOWWINDOW,
			SW_SHOWDEFAULT,0,NULL,0,0,0}

;--------------------[ Uninitialized Variables ]------------------
.data?
lpFilename		dd ?
temp			dd ?
hInstance		dd ?
OldProtected		dd ?

ProcessInfo		PROCESS_INFORMATION {}
ProcContext		CONTEXT {}

;******************************************************************
;***************************** CODE *******************************
;******************************************************************
.code
start:
	invoke	GetModuleHandle, NULL
	mov		[hInstance], eax

;Get CommandLine
	invoke	GetCommandLine
	mov		[lpFilename], eax

	cmp		byte ptr[lpFilename], 0
	jz		sp_done

	mov		esi, [lpFilename]
cl_loop:
	inc		esi
	cmp		byte ptr[esi], '"'
	jnz		cl_loop

	add		esi, 2
	mov		[lpFilename], esi

;Start Program
start_prog:
	invoke	CreateProcess, [lpFilename], ADDR [CommandLine], NULL, 
			       NULL, FALSE, NULL, NULL, NULL, 
			       addr [StartupInfo], addr [ProcessInfo]
	test	eax, eax
	jz	error_open

sp_start:
;wait till the softguard5.ocx is loaded
	invoke	Sleep, 200
;we need to get read/write access
	invoke	VirtualProtectEx,[ProcessInfo.hProcess], [Breakpoint], 
				 2, PAGE_EXECUTE_READWRITE, 
				 ADDR OldProtected
;do a little check
	invoke	ReadProcessMemory, [ProcessInfo.hProcess], 
				   [Breakpoint], addr [SaveBuffer], 2, 
				   NULL
	test	eax, eax
	jz	error_readmem

	cmp	word ptr[SaveBuffer], 15FFh
	jnz	error_notsg

;write our loop
	invoke	WriteProcessMemory, [ProcessInfo.hProcess], 
				    [Breakpoint], addr [JmpBuffer], 2, 
				    NULL
	test	eax, eax
	jz	error_writemem
	mov	[ProcContext.ContextFlags], (CONTEXT_CONTROL or CONTEXT_INTEGER)

;now we have a loop(the heart of our keygen), which checks if 
;the program has reached the breakpoint
sp_loop:
;sleep a bit, we don't want to overhead the cpu :P
	invoke	Sleep, 500d
	invoke	GetThreadContext, [ProcessInfo.hThread], 
	   			  ADDR [ProcContext]
	test	eax, eax
	jz	sp_done

;check if program has reached our breakpoint
	mov	eax, dword ptr[ProcContext.regEip]
	cmp	eax, dword ptr[Breakpoint]
	jnz	sp_loop

;ECX+8 points to our serial
	mov	eax, [ProcContext.regEcx]
	add	eax, 8
;read pointer to the right serial
	invoke	ReadProcessMemory, [ProcessInfo.hProcess], eax, 
				   addr [temp], 4, NULL
;now we have the pointer to the right serial
	invoke	ReadProcessMemory, [ProcessInfo.hProcess], [temp], 
				   addr [tempSerial], 255d, NULL

;convert unicode string to ansi-string(since VB uses unicode)
	lea	esi, [tempSerial]
	xor	ecx, ecx
sp_copyloop:
	mov	al, byte ptr[esi+ecx*2]
	mov	byte ptr[Serial+ecx], al
	inc	ecx
	cmp	byte ptr[esi+ecx*2], 0
	jnz	sp_copyloop
	
	jmp	sp_success

error_open:
	invoke	MessageBoxA, NULL, addr [err_open_text], 
			     addr [err_caption], MB_OK
	jmp	sp_done

error_notsg:
	invoke	MessageBoxA, NULL, addr [err_notsg_text], 
			     addr [err_caption], MB_OK
	invoke	TerminateProcess, [ProcessInfo.hProcess], NULL
	jmp	sp_done

error_readmem:
	invoke	MessageBoxA, NULL, addr [err_readmem_text], 
			     addr [err_caption], MB_OK
	invoke	TerminateProcess, [ProcessInfo.hProcess], NULL
	jmp	sp_done

error_writemem:
	invoke	MessageBoxA, NULL, addr [err_writemem_text], 
			     addr [err_caption], MB_OK
	invoke	TerminateProcess, [ProcessInfo.hProcess], NULL
	jmp	sp_done

sp_success:
	invoke	MessageBox, NULL, addr [Serial], 
			    addr [mb_success_capt], MB_OK
	invoke	WriteProcessMemory, [ProcessInfo.hProcess], 
				    [Breakpoint], ADDR SaveBuffer, 
				    2, NULL
	invoke	Sleep, 100
	invoke	WriteProcessMemory, [ProcessInfo.hProcess], 
				    [Breakpoint], ADDR JmpBuffer, 
				    2, NULL
	jmp	sp_loop

sp_done:
	invoke	ExitProcess, NULL
	ret
end start

------------------------------ CUT ------------------------------ 

The End
-------

That's it, I hoped you enjoyed this little tutorial and learned something :), 
maybe you have to read some passages more than one time(especially the source :))
to understand them.
If you have further questions then try to find me on irc(#learn2crack, #kraecker)

cya
sn00pee

Greets to all my 'real' friends

"Und die Moral von der Geschicht': Kommerzielle Protections lohnen sich nicht ;)"